<template>
	<li :class="getClass">
		<template v-if="!getCollapse">
			<div :class="`${prefixCls}-submenu-title`" @click.stop="handleClick" :style="getItemStyle">
				<slot name="title"></slot>
				<Icon icon="eva:arrow-ios-downward-outline" :size="14" :class="`${prefixCls}-submenu-title-icon`" />
			</div>
			<CollapseTransition>
				<ul :class="prefixCls" v-show="opened">
					<slot></slot>
				</ul>
			</CollapseTransition>
		</template>

		<Popover
			placement="right"
			:overlayClassName="`${prefixCls}-menu-popover`"
			v-else
			:visible="getIsOpend"
			@visibleChange="handleVisibleChange"
			:overlayStyle="getOverlayStyle"
			:align="{ offset: [0, 0] }"
		>
			<div :class="getSubClass" v-bind="getEvents(false)">
				<div
					:class="[
						{
							[`${prefixCls}-submenu-popup`]: !getParentSubMenu,
							[`${prefixCls}-submenu-collapsed-show-tit`]: collapsedShowTitle,
						},
					]"
				>
					<slot name="title"></slot>
				</div>
				<Icon
					v-if="getParentSubMenu"
					icon="eva:arrow-ios-downward-outline"
					:size="14"
					:class="`${prefixCls}-submenu-title-icon`"
				/>
			</div>
			<!-- eslint-disable-next-line -->
			<template #content v-show="opened">
				<div v-bind="getEvents(true)">
					<ul :class="[prefixCls, `${prefixCls}-${getTheme}`, `${prefixCls}-popup`]">
						<slot></slot>
					</ul>
				</div>
			</template>
		</Popover>
	</li>
</template>

<script lang="ts">
import type { CSSProperties, PropType } from 'vue';
import type { SubMenuProvider } from './types';
import {
	defineComponent,
	computed,
	unref,
	getCurrentInstance,
	toRefs,
	reactive,
	provide,
	onBeforeMount,
	inject,
} from 'vue';
import { useDesign } from '@/hooks/web/useDesign';
import { propTypes } from '@/utils/propTypes';
import { useMenuItem } from './useMenu';
import { useSimpleRootMenuContext } from './useSimpleMenuContext';
import { CollapseTransition } from '#/Transition';
import { Icon } from '#/components';
import { Popover } from 'ant-design-vue';
import { isBoolean, isObject } from '@/utils/is';
import mitt from 'mitt';

const DELAY = 200;
export default defineComponent({
	name: 'SubMenu',
	components: {
		Icon,
		CollapseTransition,
		Popover,
	},
	props: {
		name: {
			type: [String, Number] as PropType<string | number>,
			required: true,
		},
		disabled: propTypes.bool,
		collapsedShowTitle: propTypes.bool,
	},
	setup(props) {
		const instance = getCurrentInstance();

		const state = reactive({
			active: false,
			opened: false,
		});

		const data = reactive({
			timeout: null as TimeoutHandle | null,
			mouseInChild: false,
			isChild: false,
		});

		const { getParentSubMenu, getItemStyle, getParentMenu, getParentList } = useMenuItem(instance);

		const { prefixCls } = useDesign('menu');

		const subMenuEmitter = mitt();

		const { rootMenuEmitter } = useSimpleRootMenuContext();

		const {
			addSubMenu: parentAddSubmenu,
			removeSubMenu: parentRemoveSubmenu,
			removeAll: parentRemoveAll,
			getOpenNames: parentGetOpenNames,
			isRemoveAllPopup,
			sliceIndex,
			level,
			props: rootProps,
			handleMouseleave: parentHandleMouseleave,
		} = inject<SubMenuProvider>(`subMenu:${getParentMenu.value?.uid}`)!;

		const getClass = computed(() => {
			return [
				`${prefixCls}-submenu`,
				{
					[`${prefixCls}-item-active`]: state.active,
					[`${prefixCls}-opened`]: state.opened,
					[`${prefixCls}-submenu-disabled`]: props.disabled,
					[`${prefixCls}-submenu-has-parent-submenu`]: unref(getParentSubMenu),
					[`${prefixCls}-child-item-active`]: state.active,
				},
			];
		});

		const getAccordion = computed(() => rootProps.accordion);
		const getCollapse = computed(() => rootProps.collapse);
		const getTheme = computed(() => rootProps.theme);

		const getOverlayStyle = computed((): CSSProperties => {
			return {
				minWidth: '200px',
			};
		});

		const getIsOpend = computed(() => {
			const name = props.name;
			if (unref(getCollapse)) {
				return parentGetOpenNames().includes(name);
			}
			return state.opened;
		});

		const getSubClass = computed(() => {
			const isActive = rootProps.activeSubMenuNames.includes(props.name);
			return [
				`${prefixCls}-submenu-title`,
				{
					[`${prefixCls}-submenu-active`]: isActive,
					[`${prefixCls}-submenu-active-border`]: isActive && level === 0,
					[`${prefixCls}-submenu-collapse`]: unref(getCollapse) && level === 0,
				},
			];
		});

		function getEvents(deep: boolean) {
			if (!unref(getCollapse)) {
				return {};
			}
			return {
				onMouseenter: handleMouseenter,
				onMouseleave: () => handleMouseleave(deep),
			};
		}

		function handleClick() {
			const { disabled } = props;
			if (disabled || unref(getCollapse)) return;
			const opened = state.opened;

			if (unref(getAccordion)) {
				const { uidList } = getParentList();
				rootMenuEmitter.emit('on-update-opened', {
					opend: false,
					parent: instance?.parent,
					uidList: uidList,
				});
			} else {
				rootMenuEmitter.emit('open-name-change', {
					name: props.name,
					opened: !opened,
				});
			}
			state.opened = !opened;
		}

		function handleMouseenter() {
			const disabled = props.disabled;
			if (disabled) return;

			subMenuEmitter.emit('submenu:mouse-enter-child');

			const index = parentGetOpenNames().findIndex(item => item === props.name);

			sliceIndex(index);

			const isRoot = level === 0 && parentGetOpenNames().length === 2;
			if (isRoot) {
				parentRemoveAll();
			}
			data.isChild = parentGetOpenNames().includes(props.name);
			clearTimeout(data.timeout!);
			data.timeout = setTimeout(() => {
				parentAddSubmenu(props.name);
			}, DELAY);
		}

		function handleMouseleave(deepDispatch = false) {
			const parentName = getParentMenu.value?.props.name;
			if (!parentName) {
				isRemoveAllPopup.value = true;
			}

			if (parentGetOpenNames().slice(-1)[0] === props.name) {
				data.isChild = false;
			}

			subMenuEmitter.emit('submenu:mouse-leave-child');
			if (data.timeout) {
				clearTimeout(data.timeout!);
				data.timeout = setTimeout(() => {
					if (isRemoveAllPopup.value) {
						parentRemoveAll();
					} else if (!data.mouseInChild) {
						parentRemoveSubmenu(props.name);
					}
				}, DELAY);
			}
			if (deepDispatch) {
				if (getParentSubMenu.value) {
					parentHandleMouseleave?.(true);
				}
			}
		}

		onBeforeMount(() => {
			subMenuEmitter.on('submenu:mouse-enter-child', () => {
				data.mouseInChild = true;
				isRemoveAllPopup.value = false;
				clearTimeout(data.timeout!);
			});
			subMenuEmitter.on('submenu:mouse-leave-child', () => {
				if (data.isChild) return;
				data.mouseInChild = false;
				clearTimeout(data.timeout!);
			});

			rootMenuEmitter.on('on-update-opened', (data: boolean | (string | number)[] | Recordable) => {
				if (unref(getCollapse)) return;
				if (isBoolean(data)) {
					state.opened = data;
					return;
				}
				if (isObject(data) && rootProps.accordion) {
					const { opend, parent, uidList } = data as Recordable;
					if (parent === instance?.parent) {
						state.opened = opend;
					} else if (!uidList.includes(instance?.uid)) {
						state.opened = false;
					}
					return;
				}

				if (props.name && Array.isArray(data)) {
					state.opened = (data as (string | number)[]).includes(props.name);
				}
			});

			rootMenuEmitter.on('on-update-active-name:submenu', (data: number[]) => {
				if (instance?.uid) {
					state.active = data.includes(instance?.uid);
				}
			});
		});

		function handleVisibleChange(visible: boolean) {
			state.opened = visible;
		}

		// provide
		provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
			addSubMenu: parentAddSubmenu,
			removeSubMenu: parentRemoveSubmenu,
			getOpenNames: parentGetOpenNames,
			removeAll: parentRemoveAll,
			isRemoveAllPopup,
			sliceIndex,
			level: level + 1,
			handleMouseleave,
			props: rootProps,
		});

		return {
			getClass,
			prefixCls,
			getCollapse,
			getItemStyle,
			handleClick,
			handleVisibleChange,
			getParentSubMenu,
			getOverlayStyle,
			getTheme,
			getIsOpend,
			getEvents,
			getSubClass,
			...toRefs(state),
			...toRefs(data),
		};
	},
});
</script>
